﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Loli.Library.WinIo
{
    public class Action
    {

        /*
                    define TIMER_FREQUENCY	 1193167			//; 1,193,167 Hz
#define	OCTAVE		 2

//#define PITCH_A		440				//;  440,00 Hz
//#define PITCH_As		446				//;  466,16 Hz
//#define PITCH_H		494				//;  493,88 Hz
#define PITCH_C			 523				//;  523,25 Hz
#define PITCH_Cs		 554				//;  554,37 Hz
#define PITCH_D			 587				//;  587,33 Hz
#define PITCH_Ds		 622				//;  622,25 Hz
#define PITCH_E			 659				//;  659,25 Hz
#define PITCH_F			 698				//;  698,46 Hz
#define PITCH_Fs		 740				//;  739,99 Hz
#define PITCH_G			 784				//;  783,99 Hz
#define PITCH_Gs		 831				//;  830,61 Hz
#define PITCH_A			 880				//;  880,00 Hz
#define PITCH_As		 988				//;  987,77 Hz
#define PITCH_H			 1047			//; 1046,50 Hz

// We are going to play c-major chord

#define TONE_1			 (TIMER_FREQUENCY/(PITCH_C*OCTAVE))
#define TONE_2			 (TIMER_FREQUENCY/(PITCH_E*OCTAVE))
#define TONE_3			 (PITCH_G*OCTAVE)

*/


        private bool _IsInit = false;

        /*
        private const UInt16 KBC_KEY_CMD = 0x64;
        private const UInt16 KBC_KEY_DATA = 0x60;
        [DllImport("Lib\\WinIo32.dll")]
        private static extern bool InitializeWinIo();
        [DllImport("Lib\\WinIo32.dll")]
        private static extern bool GetPortVal(UInt16 PortAddr, out UInt32 PortVal, UInt16 Size);
        [DllImport("Lib\\WinIo32.dll")]
        private static extern bool SetPortVal(UInt16 PortAddr, UInt32 PortVal, UInt16 Size);
        [DllImport("Lib\\WinIo32.dll")]
        private static extern void ShutdownWinIo();

        */

        private int KBC_KEY_CMD = 0x64;
        private int KBC_KEY_DATA = 0x60;
        [DllImport("WinIo64.dll")]
        private static extern bool InitializeWinIo();
        [DllImport("WinIo64.dll")]
        private static extern bool GetPortVal(int wPortAddr, out int pdwPortVal, byte bSize);
        [DllImport("WinIo64.dll")]
        private static extern bool SetPortVal(int wPortAddr, int dwPortVal, byte bSize);
        [DllImport("WinIo64.dll")]
        private static extern void ShutdownWinIo();

        /*
        [DllImport("kernel32.dll")]
        private extern static IntPtr LoadLibrary(String DllName);

        [DllImport("kernel32.dll")]
        private extern static IntPtr GetProcAddress(IntPtr hModule, String ProcName);

        [DllImport("kernel32")]
        private extern static bool FreeLibrary(IntPtr hModule);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate bool InitializeWinIoType();
        /*
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate bool GetPhysLongType(IntPtr PhysAddr, UInt32* pPhysVal);
        */
        /*
      [UnmanagedFunctionPointer(CallingConvention.StdCall)]
      private delegate bool SetPhysLongType(IntPtr PhysAddr, UInt32 PhysVal);
     

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate bool GetPortValType(UInt16 PortAddr, out UInt32 pPortVal, UInt16 Size);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate bool SetPortValType(UInt16 PortAddr, UInt32 PortVal, UInt16 Size);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate bool ShutdownWinIoType();

        */
        [DllImport("kernel32.dll")]
        private static extern void Sleep(int Milliseconds);

        //  IntPtr hMod;

        private KeyDatas _KEY_DATAS = null;
        private int _INTERVAL = 200;

        public void Initialize()
        {

            if (!this._IsInit)
            {
                this._KEY_DATAS = new KeyDatas();
                bool Result = InitializeWinIo();
                if (!Result)
                {
                    // FreeLibrary(hMod);
                    throw new WinIoException(
                        "WinIo初始化错误，请确保您使用管理权限运行WINIO库文件位于可执行文件相同的目录。");

                }

                _IsInit = true;
            }



            /*
            if (IntPtr.Size == 4)
            {
                hMod = LoadLibrary("Lib\\WinIo32.dll");
            }
            else if (IntPtr.Size == 8)
            {
                hMod = LoadLibrary("Lib\\WinIo64.dll");
            }
            
            if (hMod == IntPtr.Zero)
            {
                throw new WinIoException("无法找到WINIO DLL，确保WINIO库文件位于可执行文件相同的目录。");
            }
            
            IntPtr pFunc = GetProcAddress(hMod, "InitializeWinIo");
            
            if (pFunc != IntPtr.Zero)
            {
                InitializeWinIoType InitializeWinIo = (InitializeWinIoType)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(InitializeWinIoType));
                bool Result = InitializeWinIo();

                if (!Result)
                {
                    FreeLibrary(hMod);
                    throw new WinIoException(
                        "WinIo初始化错误，请确保您使用管理权限运行WINIO库文件位于可执行文件相同的目录。");

                }
            }
            */

        }

        public void Speak(int pitch, int time)
        {
            pitch = 1193167 / pitch * 2;
            int data = 0;

            //Timer 8253-5 (AT: 8254.2).
            data = 0xb6;
            SetPortVal(0x43, data, 1);

            data = pitch & 0xff;

            SetPortVal(0x42, data, 1);

            data = (pitch >> 8) & 0xff;
            SetPortVal(0x42, data, 1);

            //speaker on
            GetPortVal(0x61, out data, 1);
            data |= 0x03;
            SetPortVal(0x61, data, 1);

            Sleep(time);

            //speaker off
            GetPortVal(0x61, out data, 1);
            data &= 0xFC;
            SetPortVal(0x61, data, 1);
        }





        public void Shutdown()
        {
            /*
            IntPtr pFunc = GetProcAddress(hMod, "ShutdownWinIo");

            if (pFunc != IntPtr.Zero)
            {
                ShutdownWinIoType ShutdownWinIo = (ShutdownWinIoType)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(ShutdownWinIoType));

                ShutdownWinIo();
                FreeLibrary(hMod);
            }*/

            if (_IsInit)
            {
                ShutdownWinIo();
                _IsInit = false;
            }

        }

        private void KBCWait4IBE()
        {
            int dwVal = 0;
            do
            {
                Sleep(4);
                bool flag = GetPortVal(KBC_KEY_CMD, out dwVal, 1);
            }
            while ((dwVal & 0x1) > 0);
        }
        /*
        private bool GetPortVal(UInt16 PortAddr, out UInt32 PortVal, UInt16 Size)
        {
            IntPtr pFunc = GetProcAddress(hMod, "GetPortVal");

            if (pFunc != IntPtr.Zero)
            {
                GetPortValType GetPortVal = (GetPortValType)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(GetPortValType));
                // Call WinIo to get value
                return GetPortVal(PortAddr, out PortVal, Size);
            }
            else
            {
                throw new WinIoException("Error returned from GetPortVal");
            }
        }
        */
        /*
        private bool SetPortVal(UInt16 PortAddr, UInt32 PortVal, UInt16 Size)
        {
            IntPtr pFunc = GetProcAddress(hMod, "SetPortVal");

            if (pFunc != IntPtr.Zero)
            {
                // PortAddr = UInt16.Parse(PortAddr, System.Globalization.NumberStyles.HexNumber);
                // PortVal = UInt32.Parse(PortVal.Text, System.Globalization.NumberStyles.HexNumber);

                SetPortValType SetPortVal = (SetPortValType)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(SetPortValType));

                // Call WinIo to set value
                return SetPortVal(PortAddr, PortVal, Size);

            }
            else
            {
                throw new WinIoException("Error returned from SetPortVal");
            }
        }*/



        // 按下指定的键，返回是否成功。
        public bool KeyDown(KeyCodes code)
        {
            KeyData keyData = this._KEY_DATAS[code] as KeyData;

            if (keyData == null)
            {
                return false;
            }

            return SendData(keyData.Make, keyData.Length >> 4, DataType.Key);
        }

        // 放开指定的键，返回是否成功。
        public bool KeyUp(KeyCodes code)
        {
            KeyData keyData = this._KEY_DATAS[code] as KeyData;
            if (keyData == null)
            {
                return false;
            }
            return SendData(keyData.Break, keyData.Length & 0xf, DataType.Key); ;
        }

        // 按下并放开指定的键，返回是否成功，
        // Interval为按下和放开之间的时间间隔。
        public bool KeyPress(KeyCodes code, int interval)
        {
            this.KeyDown(code);
            Thread.Sleep(interval);
            this.KeyUp(code);

            return true;
        }


        public bool SendData(byte[] data, int len, DataType type)
        {
            byte portVal;

            if (type == DataType.Mouse)
            {
                portVal = 0xD3;
            }
            else
            {
                portVal = 0xD2;
            }

            for (int i = 0; i < len; i++)
            {
                KBCWait4IBE();
                SetPortVal(KBC_KEY_CMD, portVal, 1);
                KBCWait4IBE();
                SetPortVal(KBC_KEY_DATA, data[i], 1);
            }
            return true;
        }

    }
}
